/*
 * Copyright (c) 2021 Huawei Device Co.,  Ltd.
 * Licensed under the Apache License,  Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,  software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,  either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "printertask.h"
#include <unistd.h>
#include "recptprinter_hilog.h"
#include "iprinteradaptor.h"
#include "recptprinter_hilog.h"
#include "PrinterLibs.h"

namespace SwanLink {
namespace Finance {

#define CHECK_STATUS_NO_RET(cond, status) do {\
    if (!(cond)) {\
        status_ = SUCCESS;\
    } else {\
        status_ = status;\
        return;\
    }\
}while(0)

static std::map<int, int> styleMap_ = {
    { PRINT_FORMAT_BOLD, 0x08 },
    { PRINT_FORMAT_REVERSE, 0x0400 },
    { PRINT_FORMAT_INVERSION, 0x0200 },
};

int convertStyle(int type, int value)
{
    if (value == 0) {
        return value;
    }
    if (styleMap_.count(type) <= 0) {
        return value;
    }
    return styleMap_[type];
}

PrinterTask::PrinterTask():
    thread_id_(-1),
    run_(false),
    status_(ERR_PRINTER_NO_TASK),
    listener_(nullptr) {
}

PrinterTask::~PrinterTask() {
    run_ = false;
    if (thread_id_ != -1) {
        pthread_join(thread_id_, NULL);
    }
    thread_id_ = -1;
}

int PrinterTask::AddTask(PrintTaskType ppt, int x, int y, PrintFormat  printFormat, const std::string& content)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (run_) {
        status_ = ERR_PRINTER_BUSY;
        return status_;
    }
    PrintItem task;
    task.ppt = ppt;
    task.x = x;
    task.y = y;
    task.fmt = printFormat;
    task.content = content;
    queTasks_.push(task);
    return SUCCESS;
}

int PrinterTask::StartPrint(int timeout, PrintListener* listener)
{
    do {
        std::lock_guard<std::mutex> lock(mutex_);
        if (run_) {
            status_ = ERR_PRINTER_BUSY;
            return status_;
        }
    }while(0);
    listener_ = listener;

    pthread_create(&thread_id_, NULL, RunTaskThread, this);
    pthread_join(thread_id_, NULL);
    thread_id_ = -1;
    listener_ = nullptr;
    return SUCCESS;
}

int PrinterTask::StopPrint()
{
    std::lock_guard<std::mutex> lock(mutex_);
    run_ = false;
    // 清空任务
    while (!queTasks_.empty()) {
        queTasks_.pop();
    }
    return SUCCESS;
}

void PrinterTask::RunTask()
{
    std::unique_lock<std::mutex> lock(mutex_);
    run_ = true;
    int total = queTasks_.size();
    int num = 0;
    while (run_) {
        if (queTasks_.empty()) {
            status_ = ERR_PRINTER_NO_TASK;
            break;
        }
        PrintItem task = queTasks_.front();
        queTasks_.pop();
        // 应允许添加新任务
        lock.unlock();
        switch (task.ppt) {
            case PT_TYPE_TEXT:
                DoTextTask(task);
                break;
            case PT_TYPE_BITMAP:
                DoBitmapTask(task);
                break;
            case PT_TYPE_QRCODE:
                DoQRTask(task);
                break;
            case PT_TYPE_BARCODE:
                DoBarcodeTask(task);
                break;
            default:
                break;
        }
        OnSuccess(++num, total);
        lock.lock();
    }
    run_ = false;
    listener_ = nullptr;
}

void PrinterTask::DoTextTask(const PrintItem &task)
{
    std::string _text = task.content;
    if (task.content.length() > 0 && task.content[task.content.length() - 1] != '\n') {
        _text.append("\n");
    }

    Pos_FeedHot(task.y*8);  // 毫米*8 = 点数
    int fontType = task.fmt.size > 1 ? 0 : 1;    // sdk值与预定义值不匹配
    int wt = 0;
    int ht = 0;
    // 字体大小
    if (task.fmt.size == PF_SIZE_EXTRASMALL) {
        wt = 0; ht = 0;
    } else if (task.fmt.size == PF_SIZE_SMALL) {
        wt = 1; ht = 1;
    } else if (task.fmt.size == PF_SIZE_MEDIUM) {
        wt = 3; ht = 3;
    } else if (task.fmt.size == PF_SIZE_LARGE) {
        wt = 5; ht = 5;
    } else if (task.fmt.size == PF_SIZE_EXTRALARGE) {
        wt = 7; ht = 7;
    } 

    bool ret = Pos_Text((char*)(_text.c_str()), 1, task.fmt.align,  wt,  ht, fontType,
        convertStyle(PRINT_FORMAT_BOLD, task.fmt.bold) |
        convertStyle(PRINT_FORMAT_INVERSION, task.fmt.inversion) |
        convertStyle(PRINT_FORMAT_REVERSE, task.fmt.reverse));
    if (ret == false) {
        OnFailed(ERR_PRINTER_HW_ERROR, "打印文本失败");
    }
    return;
}

void PrinterTask::DoBitmapTask(const PrintItem &task)
{
    Pos_Align(task.fmt.align);
    Pos_FeedHot(task.y*8);  // 毫米*8 = 点数
    bool ret = Pos_ImagePrint((char*)task.content.c_str());
    if (ret == false) {
        OnFailed(ERR_PRINTER_HW_ERROR, "打印图片失败");
    }
}

void PrinterTask::DoQRTask(const PrintItem &task)
{
    Pos_Align(task.fmt.align);
    Pos_FeedHot(task.y*8);  // 毫米*8 = 点数
    bool ret = Pos_Qrcode(task.content.c_str(), task.fmt.width, 16);

    if (ret == false) {
        OnFailed(ERR_PRINTER_HW_ERROR, "打印二维码失败");
    }
}

void PrinterTask::DoBarcodeTask(const PrintItem &task)
{
    int align = 0;
    switch (task.fmt.align) {
        case PF_ALIGN_LEFT:
        align = 1;
        break;
        case PF_ALIGN_CENTER:
        align = 2;
        break;
        case PF_ALIGN_RIGHT:
        align = 3;
        break;
    }
    Pos_FeedHot(task.y*8);  // 毫米*8 = 点数

    RECPTPRINTER_HILOGI(RECPTPRINTER_PLUGIN, "DoBarcodeTask task  align = %{public}d, width = %{public}d, height = %{public}d, text = %{public}s",
        align, task.fmt.width, task.fmt.height, task.content.c_str());
    bool ret = false;

    ret = Pos_Barcode((char*)task.content.c_str(), 0x45, align, task.fmt.width, task.fmt.height,
            0,
            task.fmt.hriloc);

    if (ret == false) {
        OnFailed(ERR_PRINTER_HW_ERROR, "打印条形码失败");
    }
}

void PrinterTask::OnFailed(int code, const std::string& err) {
    if (listener_) {
        listener_->OnFailed(code, err);
    }
}

void PrinterTask::OnSuccess(int proc, int total) {
    if (listener_) {
        listener_->OnSuccess(proc, total);
    }
}

}  // namespace Finance
}  // namespace SwanLink
